package org.altervista.cp.thinbcgen.proxy.test;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

import org.apache.bcel.Constants;
import org.apache.bcel.generic.ArrayType;
import org.apache.bcel.generic.ClassGen;
import org.apache.bcel.generic.ConstantPoolGen;
import org.apache.bcel.generic.FieldGen;
import org.apache.bcel.generic.InstructionConstants;
import org.apache.bcel.generic.InstructionFactory;
import org.apache.bcel.generic.InstructionHandle;
import org.apache.bcel.generic.InstructionList;
import org.apache.bcel.generic.MethodGen;
import org.apache.bcel.generic.ObjectType;
import org.apache.bcel.generic.PUSH;
import org.apache.bcel.generic.Type;

public class TargetProxyCreator implements Constants {
	private InstructionFactory _factory;
	private ConstantPoolGen _cp;
	private ClassGen _cg;

	public TargetProxyCreator() {
		_cg = new ClassGen(
				"org.altervista.cp.thinbcgen.proxy.test.TargetProxy",
				"java.lang.Object", "TargetProxy.java", ACC_PUBLIC | ACC_SUPER
						| ACC_ABSTRACT, new String[] {
						"org.altervista.cp.thinbcgen.proxy.test.Source",
						"org.altervista.cp.thinbcgen.proxy.UpdatableProxy" });

		_cp = _cg.getConstantPool();
		_factory = new InstructionFactory(_cg, _cp);
	}

	public void create(OutputStream out) throws IOException {
		createFields();
		createMethod_0();
		createMethod_1();
		createMethod_2();
		createMethod_3();
		createMethod_4();
		createMethod_5();
		createMethod_6();
		createMethod_7();
		_cg.getJavaClass().dump(out);
	}

	private void createFields() {
		FieldGen field;

		field = new FieldGen(ACC_PRIVATE, Type.INT, "_number", _cp);
		_cg.addField(field.getField());

		field = new FieldGen(ACC_PRIVATE, new ObjectType(
				"org.altervista.cp.thinbcgen.proxy.ExceptionHandler"), "_eh",
				_cp);
		_cg.addField(field.getField());

		field = new FieldGen(ACC_PRIVATE, Type.OBJECT, "_source", _cp);
		_cg.addField(field.getField());
	}

	private void createMethod_0() {
		InstructionList il = new InstructionList();
		MethodGen method = new MethodGen(ACC_PUBLIC, Type.INT, Type.NO_ARGS,
				new String[] {}, "unimplementedReader",
				"org.altervista.cp.thinbcgen.proxy.test.TargetProxy", il, _cp);

		InstructionHandle ih_0 = il.append(_factory
				.createNew("java.lang.UnsupportedOperationException"));
		il.append(InstructionConstants.DUP);
		InstructionHandle ih_4 = il
				.append(new PUSH(
						_cp,
						"The unimplementedReader() method is not implemented by the org.altervista.cp.proxygen.test.Source interface"));
		InstructionHandle ih_6 = il.append(_factory.createInvoke(
				"java.lang.UnsupportedOperationException", "<init>", Type.VOID,
				new Type[] { Type.STRING }, Constants.INVOKESPECIAL));
		InstructionHandle ih_9 = il.append(InstructionConstants.ATHROW);
		method.setMaxStack();
		method.setMaxLocals();
		_cg.addMethod(method.getMethod());
		il.dispose();
	}

	private void createMethod_1() {
		InstructionList il = new InstructionList();
		MethodGen method = new MethodGen(ACC_PUBLIC, Type.VOID, Type.NO_ARGS,
				new String[] {}, "unimplementedWriter",
				"org.altervista.cp.thinbcgen.proxy.test.TargetProxy", il, _cp);

		InstructionHandle ih_0 = il.append(_factory
				.createNew("java.lang.UnsupportedOperationException"));
		il.append(InstructionConstants.DUP);
		InstructionHandle ih_4 = il
				.append(new PUSH(
						_cp,
						"The unimplementedWriter() method is not implemented by the org.altervista.cp.proxygen.test.Source interface"));
		InstructionHandle ih_6 = il.append(_factory.createInvoke(
				"java.lang.UnsupportedOperationException", "<init>", Type.VOID,
				new Type[] { Type.STRING }, Constants.INVOKESPECIAL));
		InstructionHandle ih_9 = il.append(InstructionConstants.ATHROW);
		method.setMaxStack();
		method.setMaxLocals();
		_cg.addMethod(method.getMethod());
		il.dispose();
	}

	private void createMethod_2() {
		InstructionList il = new InstructionList();
		MethodGen method = new MethodGen(ACC_PUBLIC, Type.STRING,
				new Type[] { Type.INT }, new String[] { "arg0" }, "getName",
				"org.altervista.cp.thinbcgen.proxy.test.TargetProxy", il, _cp);

		InstructionHandle ih_0 = il.append(_factory.createLoad(Type.OBJECT, 0));
		il.append(_factory.createFieldAccess(
				"org.altervista.cp.thinbcgen.proxy.test.TargetProxy",
				"_source", Type.OBJECT, Constants.GETFIELD));
		il.append(_factory.createCheckCast(new ObjectType(
				"org.altervista.cp.thinbcgen.proxy.test.Source")));
		il.append(_factory.createLoad(Type.INT, 1));
		InstructionHandle ih_8 = il.append(_factory
				.createInvoke("org.altervista.cp.thinbcgen.proxy.test.Source",
						"getName", Type.STRING, new Type[] { Type.INT },
						Constants.INVOKEINTERFACE));
		il.append(_factory.createReturn(Type.OBJECT));
		InstructionHandle ih_14 = il.append(_factory
				.createStore(Type.OBJECT, 2));
		InstructionHandle ih_15 = il
				.append(_factory.createLoad(Type.OBJECT, 0));
		il.append(_factory.createFieldAccess(
				"org.altervista.cp.thinbcgen.proxy.test.TargetProxy", "_eh",
				new ObjectType(
						"org.altervista.cp.thinbcgen.proxy.ExceptionHandler"),
				Constants.GETFIELD));
		il.append(_factory.createLoad(Type.OBJECT, 0));
		il.append(_factory.createLoad(Type.OBJECT, 2));
		il.append(new PUSH(_cp, "getName"));
		InstructionHandle ih_23 = il.append(new PUSH(_cp, 1));
		il.append(_factory.createNewArray(Type.OBJECT, (short) 1));
		il.append(InstructionConstants.DUP);
		il.append(new PUSH(_cp, 0));
		il.append(_factory.createLoad(Type.INT, 1));
		il.append(_factory.createInvoke("java.lang.Integer", "valueOf",
				new ObjectType("java.lang.Integer"), new Type[] { Type.INT },
				Constants.INVOKESTATIC));
		il.append(InstructionConstants.AASTORE);
		InstructionHandle ih_34 = il.append(_factory.createInvoke(
				"org.altervista.cp.thinbcgen.proxy.ExceptionHandler", "handle",
				Type.OBJECT, new Type[] {
						new ObjectType(
								"org.altervista.cp.thinbcgen.proxy.Proxy"),
						new ObjectType("java.lang.Throwable"), Type.STRING,
						new ArrayType(Type.OBJECT, 1) },
				Constants.INVOKEINTERFACE));
		il.append(_factory.createCheckCast(Type.STRING));
		InstructionHandle ih_42 = il.append(_factory.createReturn(Type.OBJECT));
		method.addExceptionHandler(ih_0, ih_8, ih_14, new ObjectType(
				"java.lang.Throwable"));
		method.setMaxStack();
		method.setMaxLocals();
		_cg.addMethod(method.getMethod());
		il.dispose();
	}

	private void createMethod_3() {
		InstructionList il = new InstructionList();
		MethodGen method = new MethodGen(ACC_PUBLIC, Type.INT,
				new Type[] { Type.INT }, new String[] { "arg0" }, "setName",
				"org.altervista.cp.thinbcgen.proxy.test.TargetProxy", il, _cp);

		InstructionHandle ih_0 = il.append(_factory.createLoad(Type.INT, 1));
		il.append(_factory.createReturn(Type.INT));
		InstructionHandle ih_2 = il
				.append(_factory.createStore(Type.OBJECT, 2));
		InstructionHandle ih_3 = il.append(_factory.createLoad(Type.OBJECT, 0));
		il.append(_factory.createFieldAccess(
				"org.altervista.cp.thinbcgen.proxy.test.TargetProxy", "_eh",
				new ObjectType(
						"org.altervista.cp.thinbcgen.proxy.ExceptionHandler"),
				Constants.GETFIELD));
		il.append(_factory.createLoad(Type.OBJECT, 0));
		il.append(_factory.createLoad(Type.OBJECT, 2));
		il.append(new PUSH(_cp, "setName"));
		InstructionHandle ih_11 = il.append(new PUSH(_cp, 1));
		il.append(_factory.createNewArray(Type.OBJECT, (short) 1));
		il.append(InstructionConstants.DUP);
		il.append(new PUSH(_cp, 0));
		il.append(_factory.createLoad(Type.INT, 1));
		il.append(_factory.createInvoke("java.lang.Integer", "valueOf",
				new ObjectType("java.lang.Integer"), new Type[] { Type.INT },
				Constants.INVOKESTATIC));
		il.append(InstructionConstants.AASTORE);
		InstructionHandle ih_22 = il.append(_factory.createInvoke(
				"org.altervista.cp.thinbcgen.proxy.ExceptionHandler", "handle",
				Type.OBJECT, new Type[] {
						new ObjectType(
								"org.altervista.cp.thinbcgen.proxy.Proxy"),
						new ObjectType("java.lang.Throwable"), Type.STRING,
						new ArrayType(Type.OBJECT, 1) },
				Constants.INVOKEINTERFACE));
		il
				.append(_factory.createCheckCast(new ObjectType(
						"java.lang.Integer")));
		InstructionHandle ih_30 = il.append(_factory.createInvoke(
				"java.lang.Integer", "intValue", Type.INT, Type.NO_ARGS,
				Constants.INVOKEVIRTUAL));
		InstructionHandle ih_33 = il.append(_factory.createReturn(Type.INT));
		method.addExceptionHandler(ih_0, ih_0, ih_2, new ObjectType(
				"java.lang.Throwable"));
		method.setMaxStack();
		method.setMaxLocals();
		_cg.addMethod(method.getMethod());
		il.dispose();
	}

	private void createMethod_4() {
		InstructionList il = new InstructionList();
		MethodGen method = new MethodGen(ACC_PUBLIC, Type.VOID, Type.NO_ARGS,
				new String[] {}, "<init>",
				"org.altervista.cp.thinbcgen.proxy.test.TargetProxy", il, _cp);

		InstructionHandle ih_0 = il.append(_factory.createLoad(Type.OBJECT, 0));
		il.append(_factory.createInvoke("java.lang.Object", "<init>",
				Type.VOID, Type.NO_ARGS, Constants.INVOKESPECIAL));
		InstructionHandle ih_4 = il.append(_factory.createReturn(Type.VOID));
		method.setMaxStack();
		method.setMaxLocals();
		_cg.addMethod(method.getMethod());
		il.dispose();
	}

	private void createMethod_5() {
		InstructionList il = new InstructionList();
		MethodGen method = new MethodGen(ACC_PUBLIC, Type.VOID,
				new Type[] { Type.OBJECT }, new String[] { "arg0" }, "<init>",
				"org.altervista.cp.thinbcgen.proxy.test.TargetProxy", il, _cp);

		InstructionHandle ih_0 = il.append(_factory.createLoad(Type.OBJECT, 0));
		il.append(_factory.createInvoke("java.lang.Object", "<init>",
				Type.VOID, Type.NO_ARGS, Constants.INVOKESPECIAL));
		InstructionHandle ih_4 = il.append(_factory.createLoad(Type.OBJECT, 0));
		il.append(_factory.createLoad(Type.OBJECT, 1));
		il.append(_factory.createFieldAccess(
				"org.altervista.cp.thinbcgen.proxy.test.TargetProxy",
				"_source", Type.OBJECT, Constants.PUTFIELD));
		InstructionHandle ih_9 = il.append(_factory.createReturn(Type.VOID));
		method.setMaxStack();
		method.setMaxLocals();
		_cg.addMethod(method.getMethod());
		il.dispose();
	}

	private void createMethod_6() {
		InstructionList il = new InstructionList();
		MethodGen method = new MethodGen(ACC_PUBLIC, Type.INT, Type.NO_ARGS,
				new String[] {}, "getNumber",
				"org.altervista.cp.thinbcgen.proxy.test.TargetProxy", il, _cp);

		InstructionHandle ih_0 = il.append(_factory.createLoad(Type.OBJECT, 0));
		il.append(_factory.createFieldAccess(
				"org.altervista.cp.thinbcgen.proxy.test.TargetProxy",
				"_number", Type.INT, Constants.GETFIELD));
		InstructionHandle ih_4 = il.append(_factory.createReturn(Type.INT));
		method.setMaxStack();
		method.setMaxLocals();
		_cg.addMethod(method.getMethod());
		il.dispose();
	}

	private void createMethod_7() {
		InstructionList il = new InstructionList();
		MethodGen method = new MethodGen(ACC_PUBLIC, Type.VOID,
				new Type[] { new ObjectType("java.io.PrintStream") },
				new String[] { "arg0" }, "printName",
				"org.altervista.cp.thinbcgen.proxy.test.TargetProxy", il, _cp);

		InstructionHandle ih_0 = il.append(_factory.createLoad(Type.OBJECT, 1));
		il.append(_factory.createNew("java.lang.StringBuilder"));
		il.append(InstructionConstants.DUP);
		il.append(new PUSH(_cp, "My name is: "));
		il
				.append(_factory.createInvoke("java.lang.StringBuilder",
						"<init>", Type.VOID, new Type[] { Type.STRING },
						Constants.INVOKESPECIAL));
		il.append(_factory.createLoad(Type.OBJECT, 0));
		il.append(_factory.createLoad(Type.OBJECT, 0));
		il.append(_factory.createFieldAccess(
				"org.altervista.cp.thinbcgen.proxy.test.TargetProxy",
				"_number", Type.INT, Constants.GETFIELD));
		il.append(_factory.createInvoke(
				"org.altervista.cp.thinbcgen.proxy.test.TargetProxy",
				"getName", Type.STRING, new Type[] { Type.INT },
				Constants.INVOKEVIRTUAL));
		il.append(_factory.createInvoke("java.lang.String", "toUpperCase",
				Type.STRING, Type.NO_ARGS, Constants.INVOKEVIRTUAL));
		il.append(_factory.createInvoke("java.lang.StringBuilder", "append",
				new ObjectType("java.lang.StringBuilder"),
				new Type[] { Type.STRING }, Constants.INVOKEVIRTUAL));
		il.append(_factory.createInvoke("java.lang.StringBuilder", "toString",
				Type.STRING, Type.NO_ARGS, Constants.INVOKEVIRTUAL));
		il
				.append(_factory.createInvoke("java.io.PrintStream", "println",
						Type.VOID, new Type[] { Type.STRING },
						Constants.INVOKEVIRTUAL));
		InstructionHandle ih_30 = il.append(_factory.createReturn(Type.VOID));
		method.setMaxStack();
		method.setMaxLocals();
		_cg.addMethod(method.getMethod());
		il.dispose();
	}

	public static void main(String[] args) throws Exception {
		org.altervista.cp.thinbcgen.proxy.test.TargetProxyCreator creator = new org.altervista.cp.thinbcgen.proxy.test.TargetProxyCreator();
		creator.create(new FileOutputStream(
				"org.altervista.cp.thinbcgen.proxy.test.TargetProxy.class"));
	}
}
